﻿using System;
using System.Collections.Generic;
using Microsoft.Graphics.Canvas;
using Microsoft.Graphics.Canvas.Effects;
using System.Numerics;
using System.Threading.Tasks;
using TestApp2.Shaders;
using Windows.Foundation;
using Windows.Storage;
using Windows.Storage.Pickers;
using Windows.UI;
using Windows.UI.Xaml.Controls;
using Windows.Storage.Streams;
using TestApp2.Historys;
using System.Linq;
using FanKit.Transformers;

namespace TestApp2
{

    public sealed partial class MainPage : Page
    {

        BitmapLayer Layer;

        float RangeSize = 50;
        float Radius = 12;
        float Pressure = 0.5f;

        Vector2 Position;
        Rect Region;

        readonly History History = new History();

        public MainPage()
        {
            this.InitializeComponent();
            this.ConstructLiquefaction();
            this.ConstructOperator();
            this.ConstructCanvas();
            this.CanvasControl.PointerMoved += (s, e) =>
            {
                this.Pressure = e.GetCurrentPoint(this.CanvasControl).Properties.Pressure;
            };
        }

        private void ConstructLiquefaction()
        {
            this.RotateSlider.ValueChanged += (s, e) =>
            {
                this.Transformer.Radian = (float)e.NewValue / 180 * FanKit.Math.Pi;
                this.Transformer.ReloadMatrix();

                this.CanvasControl.Invalidate();
            };

            this.PressureSlider.ValueChanged += (s, e) => this.Pressure = (float)(e.NewValue / 100f);
            this.RangeSizeSlider.ValueChanged += (s, e) =>
            {
                this.RangeSize = (float)e.NewValue;
                this.Radius = (this.Layer == null) ? this.RangeSize / 2048f : this.Layer.GetRadius(this.RangeSize);

                this.CanvasControl.Invalidate();
            };

            this.ResetButton.Click += (s, e) =>
            {
                if (this.Layer == null) return;

                this.Layer.Reset();

                this.History.Clear();
                this.UndoButton.IsEnabled = this.History.CanUndo;
                this.RedoButton.IsEnabled = this.History.CanRedo;

                this.CanvasControl.Invalidate();
            };

            this.UndoButton.Click += (s, e) =>
            {
                if (this.History.CanUndo == false) return;

                bool result = this.History.Undo(this.Layer.UndoHistory);
                if (result == false) return;

                this.UndoButton.IsEnabled = this.History.CanUndo;
                this.RedoButton.IsEnabled = this.History.CanRedo;

                this.Layer.Flush();
                this.CanvasControl.Invalidate();
            };

            this.RedoButton.Click += (s, e) =>
            {
                if (this.History.CanRedo == false) return;

                bool result = this.History.Redo(this.Layer.RedoHistory);
                if (result == false) return;

                this.UndoButton.IsEnabled = this.History.CanUndo;
                this.RedoButton.IsEnabled = this.History.CanRedo;

                this.Layer.Flush();
                this.CanvasControl.Invalidate();
            };
        }

        private void ConstructCanvas()
        {
            this.CanvasControl.SizeChanged += (s, e) =>
            {
                if (e.NewSize == Size.Empty) return;
                if (e.NewSize == e.PreviousSize) return;

                this.Region = new Rect(0, 0, e.NewSize.Width, e.NewSize.Height);
                this.Transformer.ControlWidth = (float)e.NewSize.Width;
                this.Transformer.ControlHeight = (float)e.NewSize.Height;
            };
            this.CanvasControl.CreateResources += (sender, args) =>
            {
                args.TrackAsyncAction(this.CreateResourcesAsync().AsAsyncAction());
            };
            this.CanvasControl.RegionsInvalidated += (sender, args) =>
            {
                if (this.Layer == null) return;

                foreach (Rect region in args.InvalidatedRegions)
                {
                    using (CanvasDrawingSession ds = sender.CreateDrawingSession(region))
                    {
                        ds.DrawCard(new ColorSourceEffect
                        {
                            Color = Colors.White
                        }, this.Transformer, Colors.Black);

                        ds.DrawImage(new Transform2DEffect
                        {
                            BorderMode = EffectBorderMode.Hard,
                            InterpolationMode = CanvasImageInterpolation.NearestNeighbor,
                            TransformMatrix = this.Transformer.GetMatrix(),
                            Source = this.Layer.Source,
                        });
                    }
                }
            };
        }

        private async Task CreateResourcesAsync()
        {
            this.Layer = new BitmapLayer(this.CanvasControl, 1024, 1024);
            this.Radius = this.Layer.GetRadius(this.RangeSize);

            this.Transformer.Width = this.Layer.Width;
            this.Transformer.Height = this.Layer.Height;
            this.Transformer.Fit();

            this.History.Clear();
            this.UndoButton.IsEnabled = this.History.CanUndo;
            this.RedoButton.IsEnabled = this.History.CanRedo;

            this.CanvasControl.Invalidate();
        }

        private void ConstructOperator()
        {
            // Single
            this.Operator.Single_Start += (point) =>
            {
                this.Position = Vector2.Transform(point, this.Transformer.GetInverseMatrix());
                this.CanvasControl.Invalidate();
            };
            this.Operator.Single_Delta += (point) =>
            {
                Vector2 position = this.Position;
                Vector2 targetPosition = Vector2.Transform(point, this.Transformer.GetInverseMatrix());
                this.Position = targetPosition;

                this.Render(position, targetPosition);

                this.CanvasControl.Invalidate(ShaderExtensions.GetRect
                (
                    Vector2.Transform(position, this.Transformer.GetMatrix()),
                    this.RangeSize * this.Transformer.Scale,
                    this.Region
                ));
            };
            this.Operator.Single_Complete += (point) =>
            {
                if (this.Layer == null) return;

                // History
                this.History.Push(this.Layer.GetHistory());
                this.Layer.Flush();

                this.UndoButton.IsEnabled = this.History.CanUndo;
                this.RedoButton.IsEnabled = this.History.CanRedo;

                this.CanvasControl.Invalidate();
            };


            // Right
            this.Operator.Right_Start += (point) =>
            {
                this.Transformer.CacheMove(point);
                this.CanvasControl.Invalidate(); // Invalidate
            };
            this.Operator.Right_Delta += (point) =>
            {
                this.Transformer.Move(point);
                this.CanvasControl.Invalidate(); // Invalidate
            };
            this.Operator.Right_Complete += (point) =>
            {
                this.Transformer.Move(point);
                this.CanvasControl.Invalidate(); // Invalidate
            };


            // Double
            this.Operator.Double_Start += (center, space) =>
            {
                this.Transformer.CachePinch(center, space);
                this.CanvasControl.Invalidate(); // Invalidate
            };
            this.Operator.Double_Delta += (center, space) =>
            {
                this.Transformer.Pinch(center, space);

                this.CanvasControl.Invalidate(); // Invalidate
            };
            this.Operator.Double_Complete += (center, space) =>
            {
                this.CanvasControl.Invalidate(); // Invalidate
            };

            // Wheel
            this.Operator.Wheel_Changed += (point, space) =>
            {
                if (space > 0)
                    this.Transformer.ZoomIn(point, 1.05f);
                else
                    this.Transformer.ZoomOut(point, 1.05f);

                this.CanvasControl.Invalidate(); // Invalidate
            };
        }

        private void Render(Vector2 position, Vector2 targetPosition)
        {
            if (this.Layer == null) return;

            // 1. Region
            Rect rect = ShaderExtensions.GetRect(position, this.RangeSize / 2);

            // 3. Render
            this.Layer.Hit(rect);
            this.Layer.Render(position, targetPosition, this.RangeSize / 2 * this.Pressure, this.Pressure, rect);
        }

    }
}